﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;

namespace Gildor.SimpleHostMonitor.Desktop
{
    public static class ListViewSorter
    {
        #region [ Fields ]

        private static Dictionary<String, ListViewSortItem> _listViewDefinitions = new Dictionary<String, ListViewSortItem> ();

        private static ListView _lastView;
        private static GridViewColumnHeader _lastHeaderClicked;
        private static ListSortDirection _lastSortDirection;

        #endregion

        #region [ DependencyProperties ]

        public static readonly DependencyProperty CustomListViewSorterProperty = DependencyProperty.RegisterAttached (
            "CustomListViewSorter",
            typeof (String),
            typeof (ListViewSorter),
            new FrameworkPropertyMetadata ("", new PropertyChangedCallback (OnRegisterSortableGrid)));

        #region [ IsCustomListViewSorter get / set ]

        /// <summary>
        /// Gets the custom list view sorter.
        /// </summary>
        /// <param name="obj">The obj.</param>
        /// <returns></returns>
        public static String GetCustomListViewSorter (DependencyObject obj)
        {
            return (String)obj.GetValue (CustomListViewSorterProperty);
        }


        /// <summary>
        /// Sets the custom list view sorter.
        /// </summary>
        /// <param name="obj">The obj.</param>
        /// <param name="value">The value.</param>
        public static void SetCustomListViewSorter (DependencyObject obj, String value)
        {
            obj.SetValue (CustomListViewSorterProperty, value);
        }
        #endregion

        #endregion

        #region [ Public Methods ]

        /// <summary>
        /// Grids the view column header clicked handler.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        public static void GridViewColumnHeaderClickedHandler (Object sender, RoutedEventArgs e)
        {
            ListView view = _lastView = sender as ListView;
            if (view == null) return;

            ListViewSortItem listViewSortItem = (_listViewDefinitions.ContainsKey (view.Name)) ? _listViewDefinitions[view.Name] : null;
            if (listViewSortItem == null) return;

            GridViewColumnHeader headerClicked = _lastHeaderClicked = e.OriginalSource as GridViewColumnHeader;
            if (headerClicked == null) return;

            ListCollectionView collectionView = CollectionViewSource.GetDefaultView (view.ItemsSource) as ListCollectionView;
            if (collectionView == null) return;

            ListSortDirection sortDirection = _lastSortDirection = GetSortingDirection (headerClicked, listViewSortItem);

            if (headerClicked.Column.DisplayMemberBinding == null)
            {
                return;
            }

            // get header name
            String header = (headerClicked.Column.DisplayMemberBinding as Binding).Path.Path as String;
            if (String.IsNullOrEmpty (header)) return;

            // sort listview
            if (listViewSortItem.Comparer != null)
            {
                listViewSortItem.Comparer.SortBy = header;
                listViewSortItem.Comparer.SortDirection = sortDirection;
                collectionView.CustomSort = listViewSortItem.Comparer;
                view.Items.Refresh ();
            }
            else
            {
                view.Items.SortDescriptions.Clear ();
                view.Items.SortDescriptions.Add (new SortDescription (headerClicked.Column.Header.ToString (), sortDirection));
                view.Items.Refresh ();
            }

            // change datatemplate of previous and current column header
            headerClicked.Column.HeaderTemplate = GetHeaderColumnsDataTemplate (view, listViewSortItem, sortDirection);

            // Set current sort values as last sort values
            listViewSortItem.LastColumnHeaderClicked = headerClicked;
            listViewSortItem.LastSortDirection = sortDirection;
        }

        public static void Refresh ()
        {
            if (_lastView == null)
            {
                return;
            }

            ListViewSortItem listViewSortItem = (_listViewDefinitions.ContainsKey (_lastView.Name)) ? _listViewDefinitions[_lastView.Name] : null;
            if (listViewSortItem == null || listViewSortItem.LastColumnHeaderClicked == null)
            {
                return;
            }

            ListCollectionView collectionView = CollectionViewSource.GetDefaultView (_lastView.ItemsSource) as ListCollectionView;
            if (collectionView == null)
            {
                return;
            }

            if (listViewSortItem.Comparer != null)
            {
                listViewSortItem.Comparer.SortBy = (listViewSortItem.LastColumnHeaderClicked.Column.DisplayMemberBinding as Binding).Path.Path as String;
                listViewSortItem.Comparer.SortDirection = listViewSortItem.LastSortDirection;
                collectionView.CustomSort = listViewSortItem.Comparer;
                _lastView.Items.Refresh ();
            }
            else
            {
                _lastView.Items.SortDescriptions.Clear ();
                _lastView.Items.SortDescriptions.Add (new SortDescription (listViewSortItem.LastColumnHeaderClicked.Column.Header.ToString (), listViewSortItem.LastSortDirection));
                _lastView.Items.Refresh ();
            }
        }

        #endregion

        #region [ Private Methods ]

        /// <summary>
        /// Called when [register sortable grid].
        /// </summary>
        /// <param name="obj">The obj.</param>
        /// <param name="args">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> instance containing the event data.</param>
        private static void OnRegisterSortableGrid (DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            // Check if we are in design mode, if so don't do anything.
            if ((Boolean)(DesignerProperties.IsInDesignModeProperty.GetMetadata (typeof (DependencyObject)).DefaultValue)) return;

            ListView view = obj as ListView;

            if (view != null)
            {
                _listViewDefinitions.Add (view.Name, new ListViewSortItem (System.Activator.CreateInstance (Type.GetType (GetCustomListViewSorter (obj))) as IListViewCustomComparer, null, ListSortDirection.Ascending));
                view.AddHandler (GridViewColumnHeader.ClickEvent, new RoutedEventHandler (GridViewColumnHeaderClickedHandler));
            }
        }

        /// <summary>
        /// Gets the header columns data template.
        /// </summary>
        /// <param name="view">The view.</param>
        /// <param name="listViewSortItem">The list view sort item.</param>
        /// <param name="sortDirection">The sort direction.</param>
        /// <returns></returns>
        private static DataTemplate GetHeaderColumnsDataTemplate (ListView view, ListViewSortItem listViewSortItem, ListSortDirection sortDirection)
        {
            // remove mark from previous sort column
            if (listViewSortItem.LastColumnHeaderClicked != null)
                listViewSortItem.LastColumnHeaderClicked.Column.HeaderTemplate = view.TryFindResource ("listViewHeaderTemplateNoSorting") as DataTemplate;

            // set correct mark to current column
            switch (sortDirection)
            {
                case ListSortDirection.Ascending:
                    return view.TryFindResource ("listViewHeaderTemplateAscendingSorting") as DataTemplate;
                case ListSortDirection.Descending:
                    return view.TryFindResource ("listViewHeaderTemplateDescendingSorting") as DataTemplate;
                default:
                    return null;
            }
        }

        /// <summary>
        /// Gets the sorting direction.
        /// </summary>
        /// <param name="headerClicked">The header clicked.</param>
        /// <param name="listViewSortItem">The list view sort item.</param>
        /// <returns></returns>
        private static ListSortDirection GetSortingDirection (GridViewColumnHeader headerClicked, ListViewSortItem listViewSortItem)
        {
            if (headerClicked != listViewSortItem.LastColumnHeaderClicked) return ListSortDirection.Ascending;
            else
                return (listViewSortItem.LastSortDirection == ListSortDirection.Ascending) ? ListSortDirection.Descending : ListSortDirection.Ascending;
        }

        #endregion
    }
}
